package com.tom.service.Impl;

import com.tom.config.HighlightResultMapper;
import com.tom.doc.CourseDoc;
import com.tom.dto.SearchDto;
import com.tom.repository.CoueseElasticsearchRepisitory;
import com.tom.service.IESSearchService;
import com.tom.util.PageList;
import com.tom.vo.AggPageList;
import com.tom.vo.TermsAggBucket;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ESServiceImpl implements IESSearchService {
    @Autowired
    private HighlightResultMapper highlightResultMapper;

    @Autowired
    private CoueseElasticsearchRepisitory repisitory;

    @Autowired
    private ElasticsearchTemplate template;

    /**
     * es中查询
     */
    @Override
    public PageList<CourseDoc> getSearch(SearchDto searchDto) {
        // new NativeSearchQueryBuilder类可以进行查询
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();

        /**
         * search{
             分页page
             排序sort
             bool{
                must{// 相当模糊查询
                    字段查询
                }
                filter{ // 精确查询
                    字段查询
                 }
             }
         }
         */
        // 分页从0开始，总页数
        searchQueryBuilder.withPageable(PageRequest.of(searchDto.getPage()-1, searchDto.getRows()));

        String sortField = "";
        SortOrder sortOrder = SortOrder.DESC;
        if (StringUtils.isNotEmpty(searchDto.getSortField())){
            switch (searchDto.getSortField()){
                case "jg":
                    sortField = "price";
                    break;
                case "pl":
                    sortField = "commentCount";
                    break;
                case "xl":
                    sortField = "saleCount";
                    break;
                case "rq":
                    sortField = "viewCount";
                    break;
                case "xp":
                    sortField = "onlineTime";
                    break;
            }

            if (searchDto.getSortType() != null && searchDto.getSortType().equals("asc")){
                sortOrder = SortOrder.ASC;
            }
            // 排序
            searchQueryBuilder.withSort(SortBuilders.fieldSort(sortField).order(sortOrder));
        }

        // 条件查询
        // 最外层为bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 查询条件
        if (StringUtils.isNotEmpty(searchDto.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("name", searchDto.getKeyword()));
        }
        // 机构id
        if (searchDto.getCourseTypeId() != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("courseTypeId", searchDto.getCourseTypeId()));
        }
        // 机构名字
        if (searchDto.getTenantName() != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("tenantName", searchDto.getTenantName()));
        }
        // 等级
        if (searchDto.getGradeName() != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("gradeName", searchDto.getGradeName()));
        }
        // 最高价格
        if (searchDto.getPriceMax() != null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(searchDto.getPriceMax()));
        }
        // 最低价格
        if (searchDto.getPriceMin() != null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(searchDto.getPriceMin()));
        }
        // 关联查询
        searchQueryBuilder.withQuery(boolQueryBuilder);

        // 高亮查询
        HighlightBuilder.Field field = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        searchQueryBuilder.withHighlightFields(field);

        //等级聚合
        TermsAggregationBuilder gradeNameAgg = AggregationBuilders.terms("gradeNameAgg").field("gradeName");
        TermsAggregationBuilder tenantNameAgg = AggregationBuilders.terms("tenantNameAgg").field("tenantName");
        searchQueryBuilder.addAggregation(gradeNameAgg).addAggregation(tenantNameAgg);

        // searchQueryBuilder.build();这句代表执行查询操作，处理查询的结果
        AggregatedPage<CourseDoc> page = template.queryForPage(searchQueryBuilder.build(), CourseDoc.class, highlightResultMapper);
        //Page<CourseDoc> page = repisitory.search(searchQueryBuilder.build());

        // 聚合，是对于查询结果的聚合
        HashMap<String, List<TermsAggBucket>> map = new HashMap<>();
        Map<String, Aggregation> stringAggregationMap = page.getAggregations().asMap();
        stringAggregationMap.keySet().forEach(Key->{ // 这个是聚合的结果里面的名字例如gradeNameAgg，tenantNameAgg
            // 通过key在map中查询里面的value，例如机构中各种可以遍历的数值，实在不知道可以debug启动看一下
            StringTerms stringTerms = (StringTerms) stringAggregationMap.get(Key);
            // 新建集合用来装对象中的Map中的list也就是具体的每个等级的课程数量，和机构的课程数量
            ArrayList<TermsAggBucket> Lists = new ArrayList<>();

            // 遍历的是里面的map中的value，遍历的结果就是每个对象，对象里面包括查询的结果和每个结果的数量
            stringTerms.getBuckets().forEach(bucket -> {
                // 新建需要返回到前端的map中的里面list对象，为的都是封装为返回到前端的数据
                TermsAggBucket termsAggBucket = new TermsAggBucket();
                // 获取到每个机构或者说是等级的名字
                String name = bucket.getKey().toString();
                // 将名字放到对象中
                termsAggBucket.setName(name);
                // 获取每个机构或者课程的数量
                long count = bucket.getDocCount();
                // 将数量放到对象中
                termsAggBucket.setCount(count);
                // 将填充好的对象添加到list集合中
                Lists.add(termsAggBucket);
            });
            // 由于放回到前端的key和里面value的list都已经获取到，所以封装好直接返回
            map.put(Key, Lists);
        });
        // 返回到前端的数据
        return new AggPageList(page.getTotalElements(), page.getContent(),map);
    }
}
